package com.socket;

import com.alibaba.fastjson.JSON;
import com.enums.ProjectStatusEnum;
import com.os.OS;
import com.parser.PomXmlParser;
import com.utils.WebSocketJsonData;
import com.utils.WebSocketReceiveJsonData;
import lombok.extern.slf4j.Slf4j;
import org.dom4j.DocumentException;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.File;
import java.io.IOException;

/**
 * 用于运行maven项目的websocket长链接，将运行的日志情况回送给客户端
 * 这里需要注意，不能直接使用@Autowired注入bean，会抛空指针异常
 * 原因：WebSocket是多对象的，使用的spring却是单例模式。这两者刚好冲突。@Autowired注解注入对象是在启动的时候就把对象注入，
 *      而不是在使用A对象时才把A需要的B对象注入到A中。而WebSocket在刚刚有说到，有连接时才实例化对象，而且有多个连接就有多个对象。
 *      由此得知，@Autowired的bean根本就没有注入到WebSocket当中。
 * @author rkc
 * @date 2022/8/22 16:32
 */
@Slf4j
@Component
@ServerEndpoint("/api/v1/maven/run/{token}/{projectName}/{mainClass}")
public class MavenRunnerWebSocket extends RunnerWebSocket {

    private PomXmlParser parser;

    /**
     * 建立连接成功时回调
     * @param session
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("token") String token, @PathParam("projectName") String projectName, @PathParam("mainClass") String mainClass) {
        currentSession = session;
        log.info("{}加入了连接", session.getId());
        //运行maven项目
        String path = fileMappingUtil.mapping(token, projectName);
        try {
            //dom4j解析pom.xml
            parser = new PomXmlParser(path + File.separator + "pom.xml");
            //获得项目的坐标信息
            String artifactId = parser.getElementOne("project.artifactId").getText(), version = parser.getElementOne("project.version").getText();
            setStartExec(path, artifactId, version);
            currentProcess = Runtime.getRuntime().exec(cmdArray);
            log.info("{}准备启动", String.format("%s-%s.jar", artifactId, version));
            startProcess(session);
        } catch (IOException | DocumentException e) {
            log.error(e.getMessage());
            //启动失败或者运行失败
            sendMessage(new WebSocketJsonData(ProjectStatusEnum.FAILED_STARTED, e.getMessage(), null), session);
        }
    }

    private void setStartExec(String path, String artifactId, String version) {
        if (OS.isWindows()) {
            String disk = projectConfig.getStorage().substring(0, projectConfig.getStorage().indexOf(":") + 1);
            cmdArray[2] = disk + " && cd " + path + " && mvn clean && mvn package -Dmaven.test.skip=true && java -jar target/" +
                    String.format("%s-%s.jar", artifactId, version);
        } else if (OS.isLinux()) {
            cmdArray[2] = "cd " + path + " && mvn clean && mvn package -Dmaven.test.skip=true && java -jar target/" + String.format("%s-%s.jar", artifactId, version);
        }
    }

    /**
     * 连接关闭时回调
     * @param session
     */
    @OnClose
    public void onClose(Session session) {
        log.info("{}关闭了连接", session.getId());
        if (currentProcess != null && currentProcess.isAlive()) {
            stopProcess(getPidList(), session);
        }
    }

    /**
     * 收到客户端消息后的回调
     * @param message
     * @param session
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        log.info("收到{}的消息{}", session.getId(), message);
        //判断是命令类型还是输入参数类型
        WebSocketReceiveJsonData data = JSON.parseObject(message, WebSocketReceiveJsonData.class);
        if (data.getCode() == ProjectStatusEnum.COMMAND_INPUT.getCode()) {
            String s = (String) data.getData();
            if ("stop".equals(s)) {
                stopProcess(getPidList(), session);
            }
        }
    }

    /**
     * 发生错误时回调
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("id={}发生了错误", session.getId());
        if (currentProcess != null && currentProcess.isAlive()) {
            stopProcess(getPidList(), session);
        }
        error.printStackTrace();
    }
}
